/**
 * EmojiRecommender 属性测试
 * 
 * 使用 fast-check 进行属性测试，验证表情包推荐器的正确性
 */

import { describe, it, expect } from 'vitest';
import * as fc from 'fast-check';
import { EmojiRecommender } from './EmojiRecommender';
import { ALL_EMOTIONAL_TONES } from './EmojiDatabase';
import type { EmotionalTone } from '../types';

// ============================================
// 常量定义
// ============================================

const MIN_RECOMMEND_COUNT = 3;

// ============================================
// 生成器定义
// ============================================

/**
 * 生成有效的情感类型
 */
const emotionalToneArbitrary: fc.Arbitrary<EmotionalTone> = fc.constantFrom(
  ...ALL_EMOTIONAL_TONES
);

/**
 * 生成推荐数量（至少3个）
 */
const recommendCountArbitrary = fc.integer({ min: 1, max: 10 });

/**
 * 生成搜索关键词
 */
const searchQueryArbitrary = fc.string({ minLength: 1, maxLength: 20 })
  .filter(s => s.trim().length > 0)
  .map(s => s.trim());

// ============================================
// Property 4: 表情包推荐数量和分类一致性
// ============================================

describe('EmojiRecommender Property Tests', () => {
  const recommender = new EmojiRecommender();

  /**
   * **Feature: smart-chat-reply, Property 4: 表情包推荐数量和分类一致性**
   * **Validates: Requirements 3.1, 3.2, 3.3**
   * 
   * 对于任意情感类型，EmojiRecommender.recommend() 应返回至少3个表情包，
   * 且所有返回的表情包的 category 字段应与请求的情感类型一致。
   */
  describe('Property 4: 表情包推荐数量和分类一致性', () => {
    it('recommend() 应返回至少3个表情包', () => {
      fc.assert(
        fc.property(
          emotionalToneArbitrary,
          recommendCountArbitrary,
          (tone, count) => {
            const result = recommender.recommend(tone, count);
            
            // 验证返回至少 MIN_RECOMMEND_COUNT 个表情包 (Requirements 3.1)
            expect(result.emojis.length).toBeGreaterThanOrEqual(MIN_RECOMMEND_COUNT);
            
            // 验证每个表情包都有必要的字段
            for (const emoji of result.emojis) {
              expect(emoji.id).toBeDefined();
              expect(typeof emoji.id).toBe('string');
              expect(emoji.url).toBeDefined();
              expect(typeof emoji.url).toBe('string');
              expect(emoji.category).toBeDefined();
              expect(emoji.tags).toBeDefined();
              expect(Array.isArray(emoji.tags)).toBe(true);
              expect(emoji.description).toBeDefined();
            }
          }
        ),
        { numRuns: 100 }
      );
    });

    it('recommend() 返回的表情包分类应与请求的情感类型一致', () => {
      fc.assert(
        fc.property(
          emotionalToneArbitrary,
          (tone) => {
            const result = recommender.recommend(tone);
            
            // 如果不是默认推荐，所有表情包的 category 应与请求的 tone 一致
            // 或者当该类型表情包不足时，会补充其他类型
            if (!result.isDefault) {
              // 验证返回的 category 与请求的 tone 一致 (Requirements 3.2)
              expect(result.category).toBe(tone);
              
              // 验证至少有一个表情包属于请求的类型 (Requirements 3.3)
              const matchingEmojis = result.emojis.filter(e => e.category === tone);
              expect(matchingEmojis.length).toBeGreaterThan(0);
            }
          }
        ),
        { numRuns: 100 }
      );
    });

    it('当请求数量小于3时，仍应返回至少3个表情包', () => {
      fc.assert(
        fc.property(
          emotionalToneArbitrary,
          fc.integer({ min: 1, max: 2 }),
          (tone, count) => {
            const result = recommender.recommend(tone, count);
            
            // 即使请求数量小于3，也应返回至少3个 (Requirements 3.1)
            expect(result.emojis.length).toBeGreaterThanOrEqual(MIN_RECOMMEND_COUNT);
          }
        ),
        { numRuns: 50 }
      );
    });

    it('当请求数量大于3时，应返回请求的数量', () => {
      fc.assert(
        fc.property(
          emotionalToneArbitrary,
          fc.integer({ min: 4, max: 10 }),
          (tone, count) => {
            const result = recommender.recommend(tone, count);
            
            // 应返回请求的数量（如果有足够的表情包）
            expect(result.emojis.length).toBeGreaterThanOrEqual(MIN_RECOMMEND_COUNT);
          }
        ),
        { numRuns: 50 }
      );
    });
  });

  // ============================================
  // 额外的属性测试：getDefault() 行为
  // ============================================

  describe('getDefault() 行为验证', () => {
    it('getDefault() 应返回至少3个表情包且标记为默认', () => {
      // 这是一个确定性测试，不需要属性测试
      const result = recommender.getDefault();
      
      expect(result.emojis.length).toBeGreaterThanOrEqual(MIN_RECOMMEND_COUNT);
      expect(result.isDefault).toBe(true);
      expect(result.category).toBe('neutral');
    });
  });

  // ============================================
  // 额外的属性测试：search() 行为
  // ============================================

  describe('search() 行为验证', () => {
    it('search() 应返回匹配的表情包数组', () => {
      fc.assert(
        fc.property(
          searchQueryArbitrary,
          (query) => {
            const results = recommender.search(query);
            
            // 验证返回的是数组
            expect(Array.isArray(results)).toBe(true);
            
            // 验证每个结果都是有效的表情包
            for (const emoji of results) {
              expect(emoji.id).toBeDefined();
              expect(emoji.url).toBeDefined();
              expect(emoji.category).toBeDefined();
            }
          }
        ),
        { numRuns: 50 }
      );
    });

    it('search() 空字符串应返回空数组', () => {
      fc.assert(
        fc.property(
          fc.constantFrom('', '   ', '\t', '\n'),
          (emptyQuery) => {
            const results = recommender.search(emptyQuery);
            expect(results).toEqual([]);
          }
        ),
        { numRuns: 10 }
      );
    });
  });
});
